/****************************************************************************
 * Copyright (C) 2025 Chenyz. All rights reserved.
 *
 * @file    TinyOS_MemBlock.c
 *
 * @par     dependencies
 *          - TinyOS_MemBlock.h (Memory block management definitions)
 *          - TinyOS_Event.h (Event management definitions)
 *          - TinyOS_Task.h (Task management definitions)
 *          - TinyOS_Objects.h (Core data structures)
 *
 * @author  Chenyz
 *
 * @brief   Memory block management module for TinyOS with following features:
 *          - Fixed-size memory block allocation and deallocation
 *          - Task blocking and waking on memory allocation operations
 *          - Timeout support for memory allocation requests
 *          - Non-blocking memory allocation option
 *          - Memory pool management with linked list implementation
 *          - Critical section protection for thread-safe operations
 *          - Memory block information retrieval and pool deletion
 *          - Integration with event and task management systems
 *          - Cross-platform compatibility (bare-metal/RTOS systems)
 *
 * @version V1.0
 * @date    2025-05-23
 *
 * @note    Coding standard:
 *          - 4 spaces per indent level
 *          - Follow MISRA-C 2012 guidelines
 *          - Compiler compatibility: C99 and above
 *
 ****************************************************************************/

#include "TinyOS_MemBlock.h"
#include "TinyOS_Event.h"
#include "TinyOS_Task.h"
#include "TinyOS_Objects.h"


#if TINYOS_ENABLE_MEMBLOCK == 1

/**
 * @brief Initializes a memory block pool with specified parameters
 * 
 * This function sets up a memory block pool by dividing the provided memory
 * area into fixed-size blocks. Each block is initialized and added to the
 * free list for allocation.
 * 
 * @param MemBlock Pointer to the memory block pool structure
 * @param MemBlockStartAddr Starting address of the memory area to be managed
 * @param BlockSize Size of each memory block (must be at least sizeof(TinyOS_NodeType))
 * @param BlockCount Number of memory blocks to create
 */
void TinyOS_MemBlockInit(
    TinyOS_MemBlockType* MemBlock,
    uint8_t* MemBlockStartAddr,
    uint32_t BlockSize,
    uint32_t BlockCount
)
{   
    /* Validate that block size can accommodate a list node */
    if(BlockSize < sizeof(TinyOS_NodeType))
    {
        return;    
    }

    uint8_t* MemBlockStart = (uint8_t*)MemBlockStartAddr;
    uint8_t* MemBlockEnd = (MemBlockStartAddr + BlockCount * BlockSize);    
    
    /* Initialize event block and memory block pool parameters */
    TinyOS_EventBlockInit(&(MemBlock->EventBlock), TinyOS_EventTypeMemBlock);
    MemBlock->MaxMemBlockCount = BlockCount;
    MemBlock->MemBlockSize = BlockSize;
    TinyOS_ListInit(&(MemBlock->MemBlockList));
    
    /* Initialize each memory block and add to free list */
    while(MemBlockStart < MemBlockEnd)
    {
        TinyOS_NodeInit((TinyOS_NodeType*)MemBlockStart);
        TinyOS_ListTailInsert(
            &(MemBlock->MemBlockList), 
            (TinyOS_NodeType*)MemBlockStart
        );
        MemBlockStart += BlockSize;
    }
}

/**
 * @brief Allocates a memory block with optional timeout
 * 
 * This function attempts to allocate a memory block from the pool. If blocks
 * are available, it returns immediately with the allocated memory. If no blocks
 * are available, the task will block until a block becomes available or the
 * timeout expires.
 * 
 * @param MemBlock Pointer to the memory block pool structure
 * @param Memory Pointer to store the allocated memory block address
 * @param WaitTicks Maximum time to wait for a memory block (0 = no waiting)
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_MemBlockTake(
    TinyOS_MemBlockType* MemBlock,
    uint8_t** Memory,
    uint32_t WaitTicks
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();  
    
    /* Check if memory blocks are available in the pool */
    if(TinyOS_ListNodeCount(&MemBlock->MemBlockList) > 0)
    {
        /* Memory block available - allocate from free list */
        *Memory = (uint8_t*)TinyOS_ListHeadDelete(&MemBlock->MemBlockList);
        TinyOS_TaskExitCritical(Stauts); 
      
        return TinyOS_ErrorType_NoError;    
    }
    else
    {
        /* No memory blocks available - put task into waiting state */
        TinyOS_EventWait(
            &(MemBlock->EventBlock), 
            CurrentTask, 
            (void*)0, 
            TinyOS_EventTypeMemBlock, 
            WaitTicks
        );
            
        TinyOS_TaskExitCritical(Stauts); 
            
        /* Schedule another task while waiting */
        TinyOS_TaskScheduler();     
        *Memory = (uint8_t*)CurrentTask->TaskEventMsg;
            
        return CurrentTask->TaskEventWaitResult;      
    }              
}

/**
 * @brief Allocates a memory block without blocking
 * 
 * This function attempts to allocate a memory block from the pool without
 * blocking the calling task. If a block is available, it is allocated immediately.
 * If not, the function returns an error without waiting.
 * 
 * @param MemBlock Pointer to the memory block pool structure
 * @param Memory Pointer to store the allocated memory block address
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_MemBlockTakeNoWait(
    TinyOS_MemBlockType* MemBlock,
    uint8_t** Memory
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();  
    
    /* Check if memory blocks are available in the pool */
    if(TinyOS_ListNodeCount(&MemBlock->MemBlockList) > 0)
    {
        /* Memory block available - allocate from free list */
        *Memory = (uint8_t*)TinyOS_ListHeadDelete(&MemBlock->MemBlockList);
        TinyOS_TaskExitCritical(Stauts); 
      
        return TinyOS_ErrorType_NoError;    
    }
    else
    {         
        /* No memory blocks available - return error without blocking */
        TinyOS_TaskExitCritical(Stauts); 
            
        return TinyOS_ErrorType_SourceUnavailable;      
    }              
}

/**
 * @brief Returns a memory block to the pool
 * 
 * This function deallocates a memory block and returns it to the pool. If tasks
 * are waiting for memory blocks, the highest priority task is woken and receives
 * the block directly. If no tasks are waiting, the block is added to the free list.
 * 
 * @param MemBlock Pointer to the memory block pool structure
 * @param Mem Pointer to the memory block to be returned
 */
void TinyOS_MemBlockGive(    
    TinyOS_MemBlockType* MemBlock,
    uint8_t* Mem
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    
    /* Check if any tasks are waiting for memory blocks */
    if(TinyOS_EventWaitCount(&(MemBlock->EventBlock)) > 0)
    {
        /* Wake the highest priority waiting task and deliver memory block directly */
        TinyOS_TaskType* TempTaskHandler = TinyOS_EventWake(       
            &(MemBlock->EventBlock), 
            Mem, 
            TinyOS_ErrorType_NoError
        );
        
        /* Schedule immediately if woken task has higher priority */
        if(TempTaskHandler->TaskPrioity > CurrentTask->TaskPrioity)
        {         
            TinyOS_TaskScheduler();
        }
    }
    else
    {
        /* No tasks waiting - add memory block back to free list */
        TinyOS_ListTailInsert(&(MemBlock->MemBlockList), (TinyOS_NodeType*)Mem);
    }          
    
    TinyOS_TaskExitCritical(Stauts);
}

/**
 * @brief Retrieves information about a memory block pool
 * 
 * This function returns the current state of the memory block pool including
 * its maximum capacity, current available blocks, block size, and number of
 * tasks waiting for memory blocks.
 * 
 * @param MemBlock Pointer to the memory block pool structure
 * @param Info Pointer to a structure where the pool information will be stored
 */
void TinyOS_MemBlockGetInfo(
    TinyOS_MemBlockType* MemBlock,
    TinyOS_MemBlockInfo* Info
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();    

    /* Copy memory block pool state information */
    Info->MaxMemBlockCount = MemBlock->MaxMemBlockCount;
    Info->MemBlockCount = TinyOS_ListNodeCount(&(MemBlock->MemBlockList));
    Info->MemBlockSize = MemBlock->MemBlockSize;
    Info->TaskCount = TinyOS_EventWaitCount(&(MemBlock->EventBlock));
    
    TinyOS_TaskExitCritical(Stauts);
}

/**
 * @brief Deletes the memory block pool and notifies all waiting tasks
 * 
 * This function removes the memory block pool from the system, waking up all
 * tasks that were waiting for memory blocks with a deletion notification.
 * 
 * @param MemBlock Pointer to the memory block pool structure to be deleted
 * @return uint32_t Number of tasks that were waiting on the memory pool
 */
uint32_t TinyOS_MemBlockDelete(TinyOS_MemBlockType* MemBlock)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();

    /* Wake all tasks waiting on this memory pool with deletion notification */
    uint32_t DeleteCount = TinyOS_EventRemoveAll(
        &(MemBlock->EventBlock), 
        (void*)0, 
        TINYOS_TASK_DELETED
    );
    
    TinyOS_TaskExitCritical(Stauts);
        
    /* Schedule tasks if any were woken */
    if(DeleteCount > 0)
    {
        TinyOS_TaskScheduler();
    }
    
    return DeleteCount;
}

#endif
